<?php


namespace App\Service\Common;


use App\Constants\DefaultValue;
use App\Constants\ErrorCode;
use App\Constants\Model\EmployerBalance as EmployerBalanceConstants;
use App\Constants\Model\Task as TaskConstants;
use App\Constants\Model\TaskExec as TaskExecConstants;
use App\Constants\RedisKey;
use App\Dao\CooperativeDao;
use App\Dao\TaskDao;
use App\Dao\TaskExecDao;
use App\Dao\UserDao;
use App\Dao\UserEmployerDao;
use App\Dao\UserGoldLogDao;
use App\Exception\BusinessException;
use App\Model\Cooperative;
use App\Model\EmployerBalance;
use App\Model\Percentage;
use App\Model\Task;
use App\Model\User;
use App\Model\UserEmployer;
use App\Model\UserGoldLog;
use App\Utils\LevelRule;
use App\Utils\Lock;
use App\Utils\Log;
use App\Utils\Notice\Sender;
use App\Utils\Notice\UserLevelUp;
use Hyperf\Di\Annotation\Inject;
use App\Constants\Model\User as UserConstants;
use App\Constants\Model\UserGoldLog as UserGoldLogConstants;
use App\Constants\Model\Percentage as PercentageConstants;
use App\Constants\Model\Cooperative as CooperativeConstants;


/**
 * Class TaskLogic
 * @package App\Service\Common
 */
class TaskLogic
{
    /**
     * @Inject()
     * @var UserDao
     */
    protected $userDao;

    /**
     * @Inject()
     * @var UserEmployerDao
     */
    protected $userEmployerDao;

    /**
     * @Inject()
     * @var UserGoldLogDao
     */
    protected $userGoldLogDao;

    /**
     * @Inject()
     * @var TaskDao
     */
    protected $taskDao;

    /**
     * @Inject()
     * @var TaskExecDao
     */
    protected $taskExecDao;

    /**
     * @Inject()
     * @var CooperativeDao
     */
    protected $cooperativeDao;


    /**
     * 用户佣金收入操作【添加佣金，记录佣金日志，核算等级，修改主任务状态】
     *
     * 注：在事务中调用
     *
     * @param array $data
     * [
     *      'user_id'=>$userId, //用户id
     *      'task_id'=>$task_id, //任务id
     * ]
     */
    public function commissionIncome(array $data)
    {
        //内部检测参数异常
        if (!isset($data['user_id']) || !isset($data['task_id'])) {
            throw new BusinessException(-1, '给用户佣金收入操作参数异常');
        }

        $Lock = new Lock();

        $lockKeys = $Lock->locks(
            [
                RedisKey::LOCK_MYSQL_TABLE_ID . 'task:' . $data['task_id'],
                RedisKey::LOCK_MYSQL_TABLE_ID . 'user:' . $data['user_id']
            ],
            DefaultValue::LOCK_TTL
        );
        if (!$lockKeys) {
            $Lock->unlocks($lockKeys);
            throw new BusinessException(ErrorCode::LOCK_ERROR);
        }

        try {
            /** @var Task $task */
            $task = $this->taskDao->first(['id' => $data['task_id']]);

            $gold = bcmul($task->price, 100);

            //佣金收入日志
            $userGoldLog = new UserGoldLog();
            $userGoldLog->user_id = $data['user_id'];
            $userGoldLog->type = UserGoldLogConstants::TYPE_COMMISSION;
            $userGoldLog->account_type = UserGoldLogConstants::ACCOUNT_TYPE_COMMISSION_INCOME;
            $userGoldLog->gold = $gold;
            $userGoldLog->detail = '佣金收入';
            $userGoldLog->save();

            //用户添加佣金，是否升级
            /** @var User $user */
            $user = $this->userDao->first(['id' => $data['user_id']]);
            $user->commission = bcadd((string)$user->commission, (string)$gold);

            $hasCommissionSum = $this->userGoldLogDao->getCommissionSum($data['user_id']);
            $levelOld = $user->level;
            if ($user->level != UserConstants::DEFAULT_LEVEL_MAX) {
                foreach (LevelRule::$rule as $k => $v) {
                    if ($hasCommissionSum > $v['goldMin'] && $hasCommissionSum < $v['goldMax']) {
                        $user->level = $k;
                        break;
                    }
                }
            }

            if ($levelOld != $user->level) {
                //升级通知
                Sender::send(
                    [$data['user_id']],
                    new UserLevelUp(
                        [
                            'replaces' => [
                                'level' => $user->level
                            ]
                        ]
                    )
                );
            }
            $user->done_task_exec_count = $user->done_task_exec_count + 1;
            $credit_point = (int)$user->credit_point;
            //信用分+1
            $user->credit_point = ($credit_point < 100) ? ++$credit_point : 100;
            $user->save();

            //修改主任务状态，且判断主任务是否全部完成
            $this->taskIsFinish(
                [
                    'task' => $task,
                    'status' => 'user'
                ]
            );
        } catch (\Throwable $e) {
            Log::get()->error($e->getCode(), [$e->getMessage()]);
            Log::get()->error($e->getTraceAsString());
            throw new BusinessException($e->getCode(), $e->getMessage());
        } finally {
            $Lock->unlocks($lockKeys);
        }
    }


    /**
     * 任务归还【如果悬赏主已退款则返款且记录日志】
     *
     * 注：在事务中调用
     *
     * @param array $data
     * [
     *      'employer_id'=>$employerId, //悬赏主用户id
     *      'task_id'=>$task_id, //任务id
     * ]
     *
     */
    public function taskReturn(array $data)
    {
        //内部检测参数异常
        if (!isset($data['employer_id']) || !isset($data['task_id'])) {
            throw new BusinessException(-1, '用户佣金收入操作参数异常');
        }

        /** @var User $user */
        $user = $this->userDao->first(['id' => $data['employer_id']]);

        $Lock = new Lock();

        $lockKeys = $Lock->locks(
            [
                RedisKey::LOCK_MYSQL_TABLE_ID . 'task:' . $data['task_id'],
                RedisKey::LOCK_MYSQL_TABLE_ID . 'user_employer:' . $user->employer->id
            ],
            DefaultValue::LOCK_TTL
        );

        if (!$lockKeys) {
            $Lock->unlocks($lockKeys);
            throw new BusinessException(ErrorCode::LOCK_ERROR);
        }

        try {
            /** @var Task $task */
            $task = $this->taskDao->first(['id' => $data['task_id']]);

            //悬赏主已退款
            if ($task->show_status == TaskConstants::SHOW_STATUS_REFUNDED) {
                /** @var UserEmployer $userEmployer */
                $userEmployer = $this->userEmployerDao->first(['user_id' => $task->employer_id]);

                //返款 = 单价x(1+抽成比例)
                $refundPrice = bcmul(
                    (string)$task->price,
                    bcadd((string)1, (string)$task->commission_percent, 2),
                    2
                );
                $userEmployer->balance = bcadd((string)$userEmployer->balance, (string)$refundPrice, 2);
                $userEmployer->save();

                //悬赏主账户流水，逐个返款日志
                $employerBalance = new EmployerBalance();
                $employerBalance->user_id = $task->employer_id;
                $employerBalance->cooperative_id = $task->cooperative_id;
                $employerBalance->account_type = EmployerBalanceConstants::ACCOUNT_TYPE_TASK_RETURN_ONLY;
                $employerBalance->money = $refundPrice;
                $employerBalance->detail = EmployerBalanceConstants::ACCOUNT_TYPE_TEXT[EmployerBalanceConstants::ACCOUNT_TYPE_TASK_RETURN_ONLY];
                $employerBalance->save();
            }

            $this->taskIsFinish(
                [
                    'task' => $task,
                    'status' => 'employer'
                ]
            );
        } catch (\Throwable $e) {
            Log::get()->error($e->getCode(), [$e->getMessage()]);
            Log::get()->error($e->getTraceAsString());
            throw new BusinessException($e->getCode(), $e->getMessage());
        } finally {
            $Lock->unlocks($lockKeys);
        }
    }


    /**
     * 修改任务状态，且判断是否全部完成核算抽成
     * @param array $data
     * [
     *      'task'=>$task,//任务model
     *      'status'=>$status //user用户完成，employer归还悬赏主
     * ]
     */
    protected function taskIsFinish(array $data)
    {
        /** @var Task $task */
        $task = $data['task'];

        //如果当前任务全部完成，则结束
        if ($task->finish_status == TaskConstants::FINISH_STATUS_ALL) {
            return;
        }

        $hasFinish = false;

        if ($data['status'] == 'user') {
            $task->done_count = $task->done_count + 1;
        } elseif ($data['status'] == 'employer') {
            $task->remaining_count = $task->remaining_count + 1;
        }

        if ($task->finish_status == TaskConstants::FINISH_STATUS_NO) {
            //任务还在进行中，总数=已完成 即是结束
            if ($task->count == $task->done_count) {
                $hasFinish = true;
            }
        } elseif ($task->finish_status == TaskConstants::FINISH_STATUS_PART) {
            //任务未全部完成，判断子任务是否全部完成
            $taskIsNotFinish = $this->taskExecDao->select(
                [
                    'task_id' => $data['task_id'],
                    'finish_status' => TaskExecConstants::FINISH_STATUS_NO
                ]
            );
            if (count($taskIsNotFinish) == 0) {
                $hasFinish = true;
            }
        }

        if ($hasFinish) {
            /** @var Cooperative $cooperative */
            $cooperative = $this->cooperativeDao->first(['id' => $task->cooperative_id]);

            //修改任务全部完成状态
            $task->finish_status = TaskConstants::FINISH_STATUS_ALL;

            //任务完全结束核算抽成。
            $percentage = new Percentage();
            $percentage->cooperative_id = $task->cooperative_id;
            $percentage->employer_id = $task->employer_id;
            $percentage->task_id = $task->id;
            //抽成 = 单价x已完成数x抽成比例
            $percentage->price = bcmul(
                bcmul($task->price, $task->done_count, 2),
                $task->commission_percent,
                2
            );
            //是否自有商户号打款
            if ($cooperative->bind_wx_merchant == CooperativeConstants::BIND_WX_MERCHANT_NO) {
                $percentage->is_self_merchant = PercentageConstants::IS_SELF_MERCHANT_NO;
            } elseif ($cooperative->bind_wx_merchant == CooperativeConstants::BIND_WX_MERCHANT_YES) {
                $percentage->is_self_merchant = PercentageConstants::IS_SELF_MERCHANT_YES;
            }
            $percentage->save();
            //TODO 生成日志
        }

        $task->save();
    }


}